package org.orm.hsc;

import org.orm.hsc.annotations.Column;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author
 * @date 2025-04-11
 * @Description 处理结果集对象 将返回的数据封装为Java对应的类型
 */
public class ResultHandler {
    /**
     * 处理结果集对象 将上去了返回的数据封装为Java对应的类型
     * @param resultSet
     * @param rowType
     * @return
     * @param <T>
     */
    public <T> List<T> handler(ResultSet resultSet, Class<T> rowType) throws SQLException, NoSuchFieldException, InvocationTargetException, InstantiationException, IllegalAccessException {
        ArrayList<T> arrayList = new ArrayList<>();

        // 遍历结果集
        while (resultSet.next()){
            //rs中本身存储着所有的记录。
            // 从rs中 获取每一条记录
            Object row=cast(resultSet,rowType);
            arrayList.add((T)row);
        }
        return arrayList;
    }

    /**
     *  将结果集数据组成Java 对应的类型
     * @param resultSet
     * @param rowType
     * @return
     * @throws SQLException
     */
    private Object cast(ResultSet resultSet, Class<?> rowType) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException, InvocationTargetException {
        //根据类型获取对应的值
        //需要判断rowType 是那一种类型
        // 1、如果是基本数据类型（int , long , string ..) 则通过index 来获取
        // 2、如果是对象类型(domain) 则通过列名来获取 并且封装转对象进行返回
        // 处理简单类型返回
        Object value=getValueByType(resultSet,1,rowType);
        if (value!=null){
            //说明根据rowType获得了对应的数据，说明此次结果集组成的是简单类型
            // 例如： String sql = "select count(*) from t_car" ; 这样返回的只有一个值而已
            return value;
        }
        // 处理map 类型的返回
        if (Map.class.isAssignableFrom(rowType)){
            // 装载获取到的数据容器
            Map<String,Object> row=new HashMap<>();
            // 获取表对应的元数据信息
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 0; i < metaData.getColumnCount(); i++) {
                // 这里获取元数据时 可能会有两种情况
                // 一种情况是 获取到真实的字段名
                // 一种情况是 获取到字段的别名
                // demo select cno as car_no from table ;
                String key = metaData.getColumnLabel(i ); // 获取到具体的列名 car_no
//                String columnName = metaData.getColumnName(i);// 获取对应的列名 cno
                String typeName = metaData.getColumnTypeName(i); // 获取字段的对应类型
                // 我们希望通过 需要的类型来获取数据
                Object v = getValueByTypeName(resultSet, i, typeName);
                row.put(key,v);

            }
            return row;

        }
        // 代码到这里 说明处理的不是简单类型 也不是map类型
        // 处理domain类型的返回
        // 通过反射获取对象
        Object object = rowType.newInstance();
        // 获取所有的方法
        Method[] methods = rowType.getMethods();
        for (Method method : methods) {
            // 获取set 开头的方法  用于设置值
            String methodName = method.getName();
            if (methodName.startsWith("set")){
                String key  ;
                // 判断有没有通过@column注解指定字段名 demo: @Column("car_name")
                Column annotation = method.getAnnotation(Column.class);
                if (annotation!=null){
                    key = annotation.value();
                }else {
                    // 说明当前set 方法没有通过@column注解指定字段名
                    // 但是属性名上也可能会有注解
                    // 检查属性名上是否标注了@column注解
                    // 通过属性名获取属性名获取对应的属性对象
                    // 通过set方法截取对应的属性名
                    // 得到属性对象才能进行判断是否存在注解对象
                    // demo: setCno 从setCno截取Cno 还需要将Cno转为cno
                    key=methodName.substring(3);
                    // 判断当前的属性名是不是只有一个字母
                    if (key.length()==1){
                        // 转为小写
                        key=key.toLowerCase();
                    }else {
                        // 将Cno 转为 cno
                        key=key.substring(0,1).toLowerCase()+key.substring(1);
                    }
                    // 通过set方法--》截取属性名获取对应的属性对象
                    Field field = rowType.getDeclaredField(key);
                    // 判断属性对象是否存在@column注解
                    annotation = field.getAnnotation(Column.class);
                    if (annotation!=null){
                        key = annotation.value();
                    }else {
                        // 没注解 用属性名作为字段名，而这个属性名就是key
                    }
                }

                // 需要继续获取setCno(Long cno) 里面的参数类型 理论上只有一个
                Class<?> parameterType = method.getParameterTypes()[0];
                // 字段名 字段类型 拿到了 获取到对应的值
                Object valueByType = getValueByType(resultSet, key, parameterType);

                if (valueByType!=null){
                    // 给对象属性赋值
                    method.invoke(object,valueByType);
                }


            }
        }
        // 赋值完成 返回完整对象
        return object;
    }

    /**
     * 由于 resultSet.getObject() 可能会存在一些隐患
     * 例如：bigint --> BigInteger ---cast ×---> Long --> long
     * 例如：double --> BigDecimal ---cast ×---> Double --> double
     * 我们希望可以直接按照我们需要的类型来获取数据
     * 根据字段类型获取对应的值
     * @param resultSet
     * @param
     * @param columnTypeName
     * @return
     */
    private Object getValueByTypeName(ResultSet resultSet, int index, String columnTypeName) throws SQLException {
        if ("BIGINT".equals(columnTypeName)){
            return resultSet.getLong(index);
        }
        if ("DOUBLE".equals(columnTypeName)){
            return resultSet.getDouble(index);
        }
        if ("VARCHAR".equals(columnTypeName)){
            return resultSet.getString(index);
        }
        if ("INT".equals(columnTypeName)){
            return resultSet.getInt(index);
        }
        if ("CHAR".equals(columnTypeName)){
            return resultSet.getString(index);
        }
        if ("DATE".equals(columnTypeName)){
            return resultSet.getDate(index);
        }
        return null;
    }

    /**
     * 根据字段类型获取结果集的值
     * @param resultSet
     * @param key
     * @param rowType
     * @return
     */
    private Object getValueByType(ResultSet resultSet,Object key, Class<?> rowType)  {
        try {
            // int  Integer 情况
            if (rowType == int.class || rowType == Integer.class) {
                // 不知道传递的是String 类型还是 int类型 所以有两种情况
                if (key instanceof String) {
                    return resultSet.getInt((String) key);
                } else {
                    return resultSet.getInt((Integer) key);
                }
            }
            // long  Long 情况
            if (rowType == long.class || rowType == Long.class) {
                // 不知道传递的是String 类型还是 int类型 所以有两种情况
                if (key instanceof String) {
                    return resultSet.getLong((String) key);
                } else {
                    return resultSet.getLong((Integer) key);
                }
            }
            // String   情况
            if (rowType == String.class) {
                // 不知道传递的是String 类型还是 int类型 所以有两种情况
                if (key instanceof String) {
                    return resultSet.getString((String) key);
                } else {
                    return resultSet.getString((int) key);
                }
            }
            // double Double 情况
            if (rowType == double.class || rowType == Double.class) {
                // 不知道传递的是String 类型还是 int类型 所以有两种情况
                if (key instanceof String) {
                    return resultSet.getDouble((String) key);
                } else {
                    return resultSet.getDouble((Integer) key);
                }
            }
            // Date 情况
            if (rowType == java.util.Date.class) {
                // 不知道传递的是String 类型还是 int类型 所以有两种情况
                if (key instanceof String) {
                    return resultSet.getDate((String) key);
                } else {
                    return resultSet.getDate((Integer) key);
                }
            }
        }catch (SQLException e){
            // 一般情况下 上述代码产生上sql异常的原因是 无法根据key找到对应的结果集字段
            System.out.println("[warning] Column '"+key+"' not found");
            e.printStackTrace();
        }
        //含义一：结果集处理成简单类型时，返回null，表示没有处理成功，说明结果集不能处理时简单类型
        //	     要么是map，要么是domain
        //含义二：结果集处理成domain类型时，根据属性名找对应的结果集字段值，结果没有找到
        //       说明对象这个属性没有对应的结果集字段
        return null;
    }
}
